/*
 * $Id: ASCUtil.java,v 1.26 2011/11/18 19:35:28 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.common;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.InitialContext;

import com.ge.healthcare.autosc.common.database.pojo.ActRequestListUser;
import com.ge.healthcare.autosc.common.exceptions.BasicException;
import com.ge.healthcare.autosc.common.exceptions.MBeanAccessException;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.ExecResult;
import com.ge.healthcare.autosc.common.util.PropertyConstants;

/**
 * @author 212042764
 * 
 */
public class ASCUtil {
	private static final char COLON_SEPERATOR = '_';
	private static final char ZERO_PADDING = '0';
	private static byte[] INET_ADDR_LOCALHOST = null;

	/**
	 * All methods in this class should be static
	 */
	private ASCUtil() {
		super();
	}

	/**
	 * This method opens the properties file and loads it into a Properties
	 * object.
	 * 
	 * @param - String containing the absolute path of the properties file
	 * @return - Properties object containing all the name value pairs defined
	 *         in the properties file
	 */
	public static Properties loadProperties(String fileName)
			throws FileNotFoundException, IOException {
		Properties properties = new Properties();
		FileInputStream fileInputStream = null;
		try {
			// opens the properties file
			fileInputStream = new FileInputStream(fileName);
			// clears and then loads the Properties Object with the property
			// file
			properties.clear();
			properties.load(fileInputStream);
		} catch (FileNotFoundException fnfException) {
			throw fnfException;
		} catch (IOException ioException) {
			throw ioException;
		} finally {
			if (!ASCUtil.isNullOrEmptyObject(fileInputStream)) {
				// closes the file
				fileInputStream.close();
			}
		}
		return properties;
	}

	public static Properties loadPropertiesInClassPath(String fileName)
			throws IOException {
		Properties properties = new Properties();
		InputStream fileInputStream = null;
		try {
			// opens the properties file
			fileInputStream = ASCUtil.class.getResourceAsStream(fileName);
			// clears and then loads the Properties Object with the property
			// file
			properties.clear();
			properties.load(fileInputStream);
		} catch (FileNotFoundException fnfException) {
			throw fnfException;
		} catch (IOException ioException) {
			throw ioException;
		} finally {
			if (!ASCUtil.isNullOrEmptyObject(fileInputStream)) {
				// closes the file
				fileInputStream.close();
			}
		}
		return properties;
	}

	/**
	 * @param input
	 *            Object which has been
	 * @return
	 */
	public static boolean isNullOrEmptyObject(Object input) {
		if (input == null) {
			return true;
		}
		return false;
	}

	/**
	 * Returns boolean to indicate if the String is null or empty
	 * 
	 * @param input
	 * @return
	 */
	public static boolean isNullOrEmpty(String input) {
		if (input == null || "".equals(input.trim())) {
			return true;
		}
		return false;
	}

	/**
	 * Returns boolean to indicate if the passed array is null or empty
	 * 
	 * @param input
	 * @return
	 */
	public static boolean isNullOrEmptyArray(Object[] arr) {
		if (arr == null || arr.length == 0) {
			return true;
		}
		return false;
	}

	/**
	 * Checks if supplies String Buffer is null or empty
	 * 
	 * @param input
	 * @return
	 */
	public static boolean isNullOrEmptyStringBuffer(StringBuffer input) {
		if (input == null || isNullOrEmpty(input.toString())) {
			return true;
		}
		return false;
	}

	/**
	 * Returns true if Hashtable is null or empty
	 * 
	 * @param hash
	 *            - Hashtable to check whether it is null or empty
	 * @return true if Hashtable is null or empty
	 */
	public static boolean isNullOrEmptyHash(Hashtable<?, ?> hash) {
		if (hash == null || hash.isEmpty()) {
			return true;
		}
		return false;
	}

	/**
	 * Returns true if Hashmap is null or empty
	 * 
	 * @param map
	 *            - Hashmap to check whether it is null or empty
	 * @return true if Hashmap is null or empty
	 */
	public static boolean isNullOrEmptyMap(Map<?, ?> map) {
		if (map == null || map.isEmpty()) {
			return true;
		}
		return false;
	}

	/**
	 * Checks to see if the list is null or empty
	 * 
	 * @param list
	 *            - List which we want to check
	 * @return - boolean true if the list is null or empty, false if list is not
	 *         null or empty
	 */
	public static boolean isNullOrEmptyList(List<?> list) {
		if (isNullOrEmptyObject(list) || list.isEmpty()) {
			return true;
		}
		return false;
	}

	/**
	 * Checks to see if the set is null or empty
	 * 
	 * @param set
	 *            - Set which we want to check
	 * @return - boolean true if the set is null or empty, false if set is not
	 *         null or empty
	 */
	public static boolean isNullOrEmptySet(Set<?> set) {
		if (isNullOrEmptyObject(set) || set.isEmpty()) {
			return true;
		}
		return false;
	}

	/**
	 * Checks to see if properties object is null or empty
	 * 
	 * @param props
	 * @return
	 */
	public static boolean isNullOrEmptyProperties(Properties props) {
		if (isNullOrEmptyObject(props) || props.isEmpty()) {
			return true;
		}
		return false;
	}

	/**
	 * The method compares string one with you and ensure if strings match or
	 * not
	 * 
	 * @param one
	 *            - First string
	 * @param two
	 *            - Second string
	 * @return - boolean indicating whether strings match or not
	 */
	public static boolean areStringsEqual(String one, String two) {
		if (isNullOrEmpty(one) && isNullOrEmpty(two)) {
			return true;
		}
		if (isNullOrEmpty(one)) {
			return false;
		}
		if (one.equals(two)) {
			return true;
		}
		return false;
	}

	/**
	 * Checks to see if the specified value is present in the comma delimited
	 * string
	 * 
	 * @param property
	 * @param delimitor
	 * @param lookupValue
	 * @return
	 */
	public static boolean containsInProperty(String property, String delimitor,
			String lookupValue) {
		if (ASCUtil.isNullOrEmpty(lookupValue)) {
			return false;
		}
		Map<String, String> values = getMapFromProperty(property, delimitor);
		if (ASCUtil.isNullOrEmptyMap(values)) {
			return false;
		}
		return values.containsKey(lookupValue);
	}

	/**
	 * Returns map from for property after breaking the property on specified
	 * delimitor
	 * 
	 * @param property
	 * @param delimitor
	 * @return
	 */
	public static Map<String, String> getMapFromProperty(String property,
			String delimitor) {
		String value = ApplicationInfo.getInstance().getProperty(property);
		if (ASCUtil.isNullOrEmpty(value)) {
			return null;
		}
		final String[] values = value.split(delimitor);
		Map<String, String> valueHash = new HashMap<String, String>();
		for (int iCount = 0; iCount < values.length; iCount++) {
			valueHash.put(values[iCount], "");
		}
		return valueHash;
	}

	/**
	 * Return the Hostname of the machine executing
	 * 
	 * @return String name of host
	 * @throws UnknownHostException
	 */
	public static String getHardwareHostName() throws UnknownHostException {
		return InetAddress.getLocalHost().getHostName();
	}

	/**
	 * Determines if any of the specified invalid characters are present in the
	 * specified source string. This works by first eliminating the special case
	 * of an empty string and then tokenizes the source string using the invalid
	 * characters as delimiters. If the source does not contain an invalid
	 * character, only one token will have been found in the source. If there is
	 * only one token, it is possible that it is a single character and that
	 * character is one of the invalid characters. Therefore, the source is okay
	 * if the tokenizer finds only one token and that token is not found in the
	 * list of invalid characters. Weird stuff, but it works.
	 * 
	 * @param source
	 *            The string that is to be searched for invalid characters.
	 * 
	 * @param invalidChars
	 *            The list of invalid characters.
	 * 
	 * @return True if any invalid characters are found in the source string,
	 *         false if not.
	 */
	public static boolean containsInvalidChars(String source,
			String invalidChars) {
		if (source.equals("")) {
			return false;
		}
		StringTokenizer st = new StringTokenizer(source, invalidChars, true);
		if (st.countTokens() > 1) {
			return true;
		}
		return (invalidChars.indexOf(st.nextToken()) != -1);
	}

	/**
	 * Uses MBean call to get the managed server name
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String getManagedServerName() {
		String methodName = "getServerName";
		String managedServerName = null;
		InitialContext ctx = null;
		try {
			// fetch managed server name by accessing the
			// RuntimeServerMBean using the
			// MBeanServer interface
			ctx = new InitialContext();
			MBeanServer server = (MBeanServer) ctx
					.lookup("java:comp/env/jmx/runtime");
			ObjectName service = new ObjectName(
					"com.bea:Name=RuntimeService,Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean");
			managedServerName = (String) server.getAttribute(service,
					"ServerName");
		} catch (Exception mbeanAccessFailure) {
			ASCLogger
					.error(ASCUtil.class, methodName, "Failed to access MBean:"
							+ mbeanAccessFailure.getMessage(),
							mbeanAccessFailure);
			throw new MBeanAccessException(
					"Failed to access MBean to get server name:"
							+ mbeanAccessFailure.getMessage(),
					mbeanAccessFailure);
		} finally {
			if (ctx != null) {
				try {
					ctx.close();
				} catch (Exception dontCare) {
					ASCLogger.error(
							ASCUtil.class,
							methodName,
							"Failed to close initial context:"
									+ dontCare.getMessage());
				}
			}
		}
		ASCLogger.info(ASCUtil.class, methodName,
				"Returning managed server name:" + managedServerName);
		return managedServerName;
	}

	/**
	 * Returns job dir directory location for the current time
	 * 
	 * @return
	 */
	public static String getJobDir(Long Id) {
		String dirSep = ApplicationInfo.getInstance().getProperty(
				PropertyConstants.ASC_DIR_SEPERATOR);
		StringBuffer jobDir = new StringBuffer()
				.append(ApplicationInfo.getInstance().getProperty(
						PropertyConstants.ASC_MOUNT_POINT))
				.append(dirSep)
				.append(ApplicationInfo.getInstance().getProperty(
						PropertyConstants.JOBDIRS_LOCATION)).append(dirSep)
				.append(getNowAsDirTree()).append(Id).append(dirSep);
		return jobDir.toString();
	}

	/**
	 * Gets Datapool directory location
	 * 
	 * @return
	 */
	public static String getDatapoolDir() {
		String dirSep = ApplicationInfo.getInstance().getProperty(
				PropertyConstants.ASC_DIR_SEPERATOR);
		StringBuffer jobDir = new StringBuffer()
				.append(ApplicationInfo.getInstance().getProperty(
						PropertyConstants.ASC_MOUNT_POINT))
				.append(dirSep)
				.append(ApplicationInfo.getInstance().getProperty(
						PropertyConstants.DATAPOOL_LOCATION)).append(dirSep)
				.append(getNowAsDirTree());
		return jobDir.toString();
	}

	public static String getSubDir(String parentDir, Long Id) {
		String dirSep = ApplicationInfo.getInstance().getProperty(
				PropertyConstants.ASC_DIR_SEPERATOR);
		StringBuffer jobDir = new StringBuffer().append(parentDir)
				.append(dirSep).append(Id.toString()).append(dirSep);
		return jobDir.toString();
	}

	/**
	 * Returns current time as /YY/MMDD/HH/ Eg: /2009/0215/19/
	 * 
	 * @return
	 */
	private static String getNowAsDirTree() {
		String dirSep = ApplicationInfo.getInstance().getProperty(
				PropertyConstants.ASC_DIR_SEPERATOR);
		Calendar calendar = Calendar.getInstance();
		int year = calendar.get(Calendar.YEAR);
		String month = getTwoDigitMonth(calendar.get(Calendar.MONTH) + 1);
		String day = getTwoDigitDay(calendar.get(Calendar.DAY_OF_MONTH));
		String hour = getTwoDigitHour(calendar.get(Calendar.HOUR_OF_DAY));
		return new StringBuffer().append(year).append(dirSep).append(month)
				.append(day).append(dirSep).append(hour).append(dirSep)
				.toString();
	}

	/**
	 * Provides two digit representation of month
	 * 
	 * @param month
	 * @return
	 */
	public static String getTwoDigitMonth(int month) {
		String mmRep = month + "";
		if (mmRep.length() < 2) {
			mmRep = "0" + mmRep;
		}
		return mmRep;
	}

	/**
	 * Provides two day representation of day of month
	 * 
	 * @param day
	 * @return
	 */
	public static String getTwoDigitDay(int day) {
		String mmDay = day + "";
		if (mmDay.length() < 2) {
			mmDay = "0" + mmDay;
		}
		return mmDay;
	}

	/**
	 * Returns two digit hour representation
	 * 
	 * @param hour
	 * @return
	 */
	public static String getTwoDigitHour(int hour) {
		String hhRep = hour + "";
		if (hhRep.length() < 2) {
			hhRep = "0" + hhRep;
		}
		return hhRep;
	}

	/**
	 * The method returns string representation of all declared fields Problem
	 * is there is no way for an object to see private fields of its parent
	 * 
	 * @param object
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public static String getStringRep(Object object)
			throws IllegalArgumentException, IllegalAccessException {
		StringBuffer definition = new StringBuffer();
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (!ASCUtil.isNullOrEmpty(definition.toString())) {
				definition.append(",");
			}
			field.setAccessible(true);
			definition.append("{").append(field.getName()).append("=")
					.append(field.get(object)).append("}");
		}
		return definition.toString();
	}

	/**
	 * The methods prints the entire description of the class, pass System.out
	 * as PrintStream to print the class to console
	 * 
	 * @param object
	 * @param out
	 */
	public static void describeInstance(Object object, PrintStream out) {
		Class<?> clazz = object.getClass();

		Constructor<?>[] constructors = clazz.getDeclaredConstructors();
		Field[] fields = clazz.getDeclaredFields();
		Method[] methods = clazz.getDeclaredMethods();

		out.println("Description for class: " + clazz.getName());
		out.println();
		out.println("Summary");
		out.println("-----------------------------------------");
		out.println("Constructors: " + (constructors.length));
		out.println("Fields: " + (fields.length));
		out.println("Methods: " + (methods.length));

		out.println();
		out.println();
		out.println("Details");
		out.println("-----------------------------------------");

		if (constructors.length > 0) {
			out.println();
			out.println("Constructors:");
			for (Constructor<?> constructor : constructors) {
				out.println(constructor);
			}
		}

		if (fields.length > 0) {
			out.println();
			out.println("Fields:");
			for (Field field : fields) {
				out.println(field);
			}
		}

		if (methods.length > 0) {
			out.println();
			out.println("Methods:");
			for (Method method : methods) {
				out.println(method);
			}
		}
	}

	/**
	 * Returns a unique identifier
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws UnknownHostException
	 */
	public static String getUID(Object object) throws NoSuchAlgorithmException,
			UnknownHostException {
		String strRetVal = "";
		String strTemp = "";
		// Get IPAddress Segment
		byte[] ipaddr = null;
		if (ASCUtil.isNullOrEmptyObject(INET_ADDR_LOCALHOST)) {
			InetAddress addr = InetAddress.getLocalHost();
			ipaddr = addr.getAddress();
			INET_ADDR_LOCALHOST = ipaddr;
		} else {
			ipaddr = INET_ADDR_LOCALHOST;
		}

		for (int i = 0; i < ipaddr.length; i++) {
			Byte b = ipaddr[i];
			strTemp = Integer.toHexString(b.intValue() & 0x000000ff);
			while (strTemp.length() < 2) {
				strTemp = ZERO_PADDING + strTemp;
			}
			strRetVal += strTemp;
		}
		strRetVal += COLON_SEPERATOR;
		// Get CurrentTimeMillis() segment
		strTemp = Long.toHexString(System.currentTimeMillis());
		while (strTemp.length() < 12) {
			strTemp = ZERO_PADDING + strTemp;
		}
		strRetVal = new StringBuffer().append(strRetVal).append(strTemp)
				.append(COLON_SEPERATOR).toString();
		// Get Random Segment
		SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
		strTemp = Integer.toHexString(prng.nextInt());
		while (strTemp.length() < 8) {
			strTemp = ZERO_PADDING + strTemp;
		}
		strRetVal = new StringBuffer().append(strRetVal)
				.append(strTemp.substring(4)).append(COLON_SEPERATOR)
				.toString();
		// Get IdentityHash() segment
		strTemp = Long.toHexString(System.identityHashCode((Object) object));
		while (strTemp.length() < 8) {
			strTemp = ZERO_PADDING + strTemp;
		}
		strRetVal += strTemp;
		return strRetVal.toUpperCase();
	}

	/**
	 * Silently close a stream. This closes the specified stream, ignoring any
	 * errors.
	 * 
	 * @param stream
	 *            The stream to be closed.
	 */
	public static void silentlyClose(InputStream stream) {
		if (stream != null) {
			try {
				stream.close();
			} catch (IOException ignoreMe) {
				// do nothing
			}
		}
	}

	/**
	 * Silently close a stream. This closes the specified stream, ignoring any
	 * errors.
	 * 
	 * @param stream
	 *            The stream to be closed.
	 */
	public static void silentlyClose(PrintWriter writer) {
		if (writer != null) {
			writer.close();
			if (writer.checkError()) {
				ASCLogger.warn(ASCUtil.class, "silentlyClose",
						"Error closing writer, continue..");
			}
		}
	}

	/**
	 * Run the specified shell command. This executes the command, waits for it
	 * to complete, and returns its results.
	 * 
	 * @param command
	 *            The shell command to run.
	 * 
	 * @param envList
	 *            Environment variables to apply to the command. Each element in
	 *            the array is an environment variable in the form name=value.
	 *            If there there are no environment variables to define, use an
	 *            empty string array.
	 * 
	 * @return The results of running the specified command.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected executing the command, or an error
	 *                is detected gathering the results from the command. It is
	 *                the caller's responsibility to report an error if an
	 *                exception is thrown.
	 */
	public static ExecResult runShellCommand(String command, String[] envList)
			throws BasicException {
		ASCLogger.info("Running shell command: " + command);
		String[] commandList = new String[3];
		commandList[0] = "sh";
		commandList[1] = "-c";
		commandList[2] = command;
		return new ExecResult(commandList, envList);
	}

	/**
	 * Generate a string representation of a string array.
	 * 
	 * @param theArray
	 *            The array to represent as a string.
	 * 
	 * @return The array represented as a string.
	 */
	public static String toString(String[] theArray) {
		StringBuffer buffer = new StringBuffer();
		buffer.append('{');
		for (int idx = 0; idx < theArray.length; idx += 1) {
			if (idx != 0) {
				buffer.append(',');
			}
			buffer.append(theArray[idx]);
		}
		buffer.append('}');
		return buffer.toString();
	}

	/**
	 * Returns sent from user as present in list
	 * 
	 * @param userLst
	 * @return
	 */
	public static String getSentFromUserInList(List<ActRequestListUser> userLst) {
		for (ActRequestListUser user : userLst) {
			if (ActRequestListUser.SENT_FROM_ADDRESS_TYPE.equalsIgnoreCase(user
					.getAddressType())) {
				return user.getEmailAddress();
			}
		}
		return null;
	}

	/**
	 * Returns SSO Id of sender
	 * 
	 * @param userLst
	 * @return
	 */
	public static Integer getSendersSSO(List<ActRequestListUser> userLst) {
		for (ActRequestListUser user : userLst) {
			if (ActRequestListUser.SENT_FROM_ADDRESS_TYPE.equalsIgnoreCase(user
					.getAddressType())) {
				return user.getSso();
			}
		}
		return null;
	}

	/**
	 * Returns SSO Id of sender
	 * 
	 * @param userLst
	 * @return
	 */
	public static String getSendersEmailAddress(List<ActRequestListUser> userLst) {
		for (ActRequestListUser user : userLst) {
			if (ActRequestListUser.SENT_FROM_ADDRESS_TYPE.equalsIgnoreCase(user
					.getAddressType())) {
				return user.getEmailAddress();
			}
		}
		return null;
	}

	/**
	 * Returns reply to addresses as present in list
	 * 
	 * @param userLst
	 * @return
	 */
	public static String getReplyToAddresses(List<ActRequestListUser> userLst) {
		StringBuffer replyToAddresses = new StringBuffer();
		for (ActRequestListUser user : userLst) {
			if (ActRequestListUser.SENT_FROM_ADDRESS_TYPE.equalsIgnoreCase(user
					.getAddressType())) {
				continue;
			}
			if (ASCUtil.isNullOrEmpty(replyToAddresses.toString())) {
				replyToAddresses.append(user.getEmailAddress());
			} else {
				replyToAddresses.append(",").append(user.getEmailAddress());
			}
		}
		return replyToAddresses.toString();
	}

	/**
	 * Converts stack trace to string
	 * @param throwable
	 * @return
	 */
	public static String getStackTrace(Throwable throwable) {
		Writer result = new StringWriter();
		PrintWriter printWriter = new PrintWriter(result);
		throwable.printStackTrace(printWriter);
		return result.toString();
	}
}
